package com.botarena.server.services;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;

import com.botarena.server.dao.BattleDAO;
import com.botarena.server.dao.BattleProcessDAO;
import com.botarena.server.dao.BotDAO;
import com.botarena.server.dao.helper.NonTransactional;
import com.botarena.server.dao.helper.NonTransactionalReturn;
import com.botarena.server.model.Battle;
import com.botarena.server.model.BattleProcess;
import com.botarena.server.model.Bot;
import com.botarena.server.services.pythonHelpers.BattleResultHolder;
import com.botarena.shared.BattleTurn;
import com.google.appengine.api.datastore.Key;

/**
 * Static Battle service. Performs general Battle manipulation.
 */
public class BattleService {
	private static final Logger log = Logger.getLogger(BattleService.class
			.getName());

	public static void storeBattle(final BattleResultHolder result,
			final Key contestKey) {

		log.info("Storing battle result. " + result.getBot1Key() + " vs. "
				+ result.getBot2Key() + ". Winner: " + result.getWinnerKey());

		final Key battleProcessKey = DAOService
				.execute(new NonTransactionalReturn<Key>() {

					@Override
					public Key doOutsideTransaction(PersistenceManager pm) {
						BattleProcessDAO battleProcessDAO = new BattleProcessDAO(
								pm);
						BattleProcess p = new BattleProcess(result.getProcess());
						p.setActive(true);
						battleProcessDAO.create(p);
						return p.getKey();
					}
				});

		log.info("Storing battle result. BattleProcess(" + result.getProcess()
				+ ") stored successfully with key: " + battleProcessKey);

		final Date now = new Date();

		DAOService.execute(new NonTransactional() {

			@Override
			public void doOutsideTransaction(PersistenceManager pm) {

				BattleDAO battleDAO = new BattleDAO(pm);
				Battle b = new Battle();
				b.setBot1(result.getBot1Key());
				b.setBot2(result.getBot2Key());
				b.setContest(contestKey);
				b.setWinner(result.getWinnerKey());
				b.setActive(true);
				b.setProcess(battleProcessKey);
				b.setTime(now);
				b.setJustification(result.getJustification());
				battleDAO.create(b);
				log.info("Storing battle result. Battle("
						+ result.getJustification()
						+ ") stored successfully with key: " + b.getKey());
			}
		});

		DAOService.execute(new NonTransactional() {

			@Override
			public void doOutsideTransaction(PersistenceManager pm) {
				BotDAO botDAO = new BotDAO(pm);
				Bot bot1 = botDAO.getByKey(result.getBot1Key());
				bot1.setLastFight(now);
				Bot bot2 = botDAO.getByKey(result.getBot2Key());
				bot2.setLastFight(now);

				int delta = bot1.getRank() - bot2.getRank();
				double we = 1 / (1 + Math.pow(10, delta / 400.0));
				log
						.info("Storing battle result. Computing ranking modification: delta="
								+ delta + ", we=" + we + ".");

				double wy1 = result.getWinnerKey() == null ? 0.5 : (result
						.getWinnerKey().equals(bot1.getKey()) ? 1.0 : 0.0);
				double diff1 = wy1 - we;
				log
						.info("Storing battle result. Computing ranking modification: ranking of bot1="
								+ bot1.getRank()
								+ ", wy1="
								+ wy1
								+ ", diff1="
								+ diff1);
				bot1.setRank(bot1.getRank() + (int) (32 * diff1));
				log
						.info("Storing battle result. Computing ranking modification: new ranking of bot1="
								+ bot1.getRank());

				double wy2 = result.getWinnerKey() == null ? 0.5 : (result
						.getWinnerKey().equals(bot2.getKey()) ? 1.0 : 0.0);
				double diff2 = wy2 - we;
				log
						.info("Storing battle result. Computing ranking modification: ranking of bot2="
								+ bot2.getRank()
								+ ", wy2="
								+ wy2
								+ ", diff2="
								+ diff2);
				bot2.setRank(bot2.getRank() + (int) (32 * diff2));
				log
						.info("Storing battle result. Computing ranking modification: new ranking of bot2="
								+ bot2.getRank());

				if (result.getWinnerKey() == null) {
					log
							.info("Storing battle result. Computing ranking modification. The result is draw. draws of bot1="
									+ bot1.getDraws()
									+ ", draws of bot2="
									+ bot2.getDraws());
					bot1.setDraws(bot1.getDraws() + 1);
					bot2.setDraws(bot2.getDraws() + 1);
					log
							.info("Storing battle result. Computing ranking modification. New draws of bot1="
									+ bot1.getDraws()
									+ ", new draws of bot2="
									+ bot2.getDraws());
				} else {
					log
							.info("Storing battle result. Computing ranking modification. Result is not draw. Bot1 is winner? "
									+ result.getWinnerKey().equals(
											bot1.getKey())
									+ ", Bot2 is winner? "
									+ result.getWinnerKey().equals(
											bot2.getKey()));
					if (result.getWinnerKey().equals(bot1.getKey())) {
						log.info("Storing battle result. Bot1: (winner) "
								+ bot1.getWins() + "/" + bot1.getLosses()
								+ ", Bot2: (loser) " + bot2.getWins() + "/"
								+ bot2.getLosses());
						bot2.setLosses(bot2.getLosses() + 1);
						bot1.setWins(bot1.getWins() + 1);
						log.info("Storing battle result. New Bot1: (winner) "
								+ bot1.getWins() + "/" + bot1.getLosses()
								+ ", New Bot2: (loser) " + bot2.getWins() + "/"
								+ bot2.getLosses());
					} else {
						log.info("Storing battle result. Bot1: (loser) "
								+ bot1.getWins() + "/" + bot1.getLosses()
								+ ", Bot2: (winner) " + bot2.getWins() + "/"
								+ bot2.getLosses());
						bot1.setLosses(bot1.getLosses() + 1);
						bot2.setWins(bot2.getWins() + 1);
						log.info("Storing battle result. New Bot1: (loser) "
								+ bot1.getWins() + "/" + bot1.getLosses()
								+ ", New Bot2: (winner) " + bot2.getWins()
								+ "/" + bot2.getLosses());
					}
				}
			}
		});

		log.info("Storing battle result complete.");
	}

	public static List<Battle> getLatestBattles(final Key contestKey,
			final int count) {
		return DAOService.execute(new NonTransactionalReturn<List<Battle>>() {

			@Override
			public List<Battle> doOutsideTransaction(PersistenceManager pm) {
				BattleDAO dao = new BattleDAO(pm);

				List<Battle> battles = new ArrayList<Battle>(dao.getLastest(
						contestKey, count));

				return new ArrayList<Battle>(pm.detachCopyAll(battles));
			}

		});
	}
	
	public static List<Battle> getAllBattles(final Key contestKey) {
		return DAOService.execute(new NonTransactionalReturn<List<Battle>>() {

			@Override
			public List<Battle> doOutsideTransaction(PersistenceManager pm) {
				BattleDAO dao = new BattleDAO(pm);
				// getting at most 500 battles
				Collection<Battle> battles = dao.getLastest(contestKey, 500);
				
				return new ArrayList<Battle>(pm.detachCopyAll(battles));
			}
		});
	}
	
	public static List<Battle> getBotBattles(final Key botKey) {
		return DAOService.execute(new NonTransactionalReturn<List<Battle>>() {

			@Override
			public List<Battle> doOutsideTransaction(PersistenceManager pm) {
				BattleDAO dao = new BattleDAO(pm);
				// getting at most 500 battles
				Collection<Battle> battles = dao.getBotFights(botKey, 250);
				
				
				return new ArrayList<Battle>(pm.detachCopyAll(battles));
			}
		});
	}
	
	public static List<Battle> getBattlesByKey(final List<String> keys) {
		return DAOService.execute(new NonTransactionalReturn<List<Battle>>() {

			@Override
			public List<Battle> doOutsideTransaction(PersistenceManager pm) {
				BattleDAO dao = new BattleDAO(pm);
				// getting at most 500 battles
				List<Battle> battles = new ArrayList<Battle>(keys.size());
				for(String key : keys)
					battles.add(dao.getByKey(key));
				
				return new ArrayList<Battle>(pm.detachCopyAll(battles));
			}
		});
	}

	public static Battle getBattleByKey(final String battleKey) {
		return DAOService.execute(new NonTransactionalReturn<Battle>() {

			@Override
			public Battle doOutsideTransaction(PersistenceManager pm) {
				BattleDAO battleDAO = new BattleDAO(pm);
				return pm.detachCopy(battleDAO.getByKey(battleKey));
			}
		});
	}

	public static List<BattleTurn> getBattleProcess(final String battleKey) {
		return DAOService
				.execute(new NonTransactionalReturn<List<BattleTurn>>() {

					@Override
					public List<BattleTurn> doOutsideTransaction(
							PersistenceManager pm) {
						BattleDAO battleDAO = new BattleDAO(pm);
						Battle battle = battleDAO.getByKey(battleKey);

						BattleProcess process = new BattleProcessDAO(pm)
								.getByKey(battle.getProcess());

						return process.getMoves();

						/*
						 * BotDAO botDAO = new BotDAO(pm); ContestDAO contestDAO
						 * = new ContestDAO(pm); String contest =
						 * contestDAO.getByKey( battle.getContest()).getName();
						 * String name1 = botDAO.getByKey(battle.getBot1())
						 * .getName(); String name2 =
						 * botDAO.getByKey(battle.getBot2()) .getName();
						 * 
						 * String strDate = new
						 * SimpleDateFormat("dd.MM.yyyy HH:mm:ss"
						 * ).format(battle.getTime());
						 * 
						 * BattlePreviewInfo info = new BattlePreviewInfo(
						 * battleKey, name1, name2, battle.getWinnerNum(),
						 * contest, strDate, process.getMoves());
						 */

					}

				});
	}

}
